package gui;

import java.awt.DisplayMode;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.RepaintManager;

/**
 * The frame which holds the game's graphical content.
 * 
 * @author Jason
 * 
 */
public class GameFrame extends JFrame {

	static {
		RepaintManager.setCurrentManager(new EmptyRepaintManager());
	}

	private static final int DEFAULT_WIDTH = 800, DEFAULT_HEIGHT = 600;

	private final GraphicsDevice device;

	public GameFrame() {
		this("My Game");
	}

	public GameFrame(String title) {
		this(title, true);
	}

	public GameFrame(String title, boolean showFrameTop) {
		this(title, DEFAULT_WIDTH, DEFAULT_HEIGHT, showFrameTop);
	}

	public GameFrame(String title, int width, int height, boolean showFrameTop) {
		super(title);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setIgnoreRepaint(true);
		setResizable(false);
		GraphicsEnvironment environment = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		device = environment.getDefaultScreenDevice();
		setUndecorated(!showFrameTop);
		setSize(width, height);
		setLocationRelativeTo(null); // centers the frame
	}

	@Override
	public void setVisible(boolean b) {
		super.setVisible(b);
		createBufferStrategy(2);
	}

	public void setFullScreen(boolean b) {
		if (b)
			device.setFullScreenWindow(this);
		else
			device.setFullScreenWindow(null);
	}

	/**
	 * Gets the graphics object for drawing. Call update() after drawing to show
	 * changes. Be sure to dispose() of this graphics object when done.
	 */
	@Override
	public Graphics2D getGraphics() {
		BufferStrategy strategy = getBufferStrategy();
		if (strategy == null)
			return null;
		return (Graphics2D) strategy.getDrawGraphics();
	}

	/**
	 * Updates the screen
	 */
	public void update() {
		BufferStrategy strategy = getBufferStrategy();
		if (!strategy.contentsLost()) {
			strategy.show();
		}
	}

	public void setDisplayMode(DisplayMode displayMode) {
		if (device.isDisplayChangeSupported()) {
			try {
				device.setDisplayMode(displayMode);
			} catch (IllegalArgumentException ex) {
			}
			setSize(displayMode.getWidth(), displayMode.getHeight());
		}
	}

	/**
	 * @return A list of possible display modes.
	 */
	public DisplayMode[] getDisplayModes() {
		return device.getDisplayModes();
	}

	/**
	 * @return The display mode with the largest resolution, bit-depth, and
	 *         refresh-rate in that order of importance.
	 */
	public DisplayMode getBestDisplayMode() {
		DisplayMode[] modes = getDisplayModes();
		if (modes.length == 0)
			return null;
		else if (modes.length == 1)
			return modes[0];
		boolean smallToLarge = modes[0].getWidth() * modes[0].getHeight() < modes[1]
				.getWidth()
				* modes[1].getHeight();
		ArrayList<DisplayMode> possible = new ArrayList<DisplayMode>();
		possible.add(modes[modes.length - 1]);
		if (smallToLarge) {
			for (int i = modes.length - 2; i >= 0; i--) {
				if (modes[i].getWidth() < possible.get(0).getWidth())
					break;
				else
					possible.add(modes[i]);
			}
		} else {
			for (int i = 0; i < modes.length; i++) {
				if (modes[i].getWidth() < possible.get(0).getWidth())
					break;
				else
					possible.add(modes[i]);
			}
		}
		int bestDisplayIndex = 0;
		int bestRefreshRate = possible.get(0).getRefreshRate();
		int bestBitDepth = possible.get(0).getBitDepth();
		for (int i = 1; i < possible.size(); i++) {
			int bitDepth = possible.get(i).getBitDepth();
			int refreshRate = possible.get(i).getRefreshRate();
			if (bitDepth > bestBitDepth
					|| (bitDepth == bestBitDepth && refreshRate > bestRefreshRate))
				bestDisplayIndex = i;
			bestRefreshRate = refreshRate;
			bestBitDepth = bitDepth;
		}
		return possible.get(bestDisplayIndex);
	}

}
